package com.ww.springboot.boot.jvm.heap;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

/**
 * 堆结构简单模拟
 *
 * 模拟难点：
 * 1.垃圾回收策略模拟
 * 2.经历GC次数
 *
 * @author wanwei
 * @since 2021-03-26 14:41
 */
public class HeapDemo1 {

    /**
     * 最大堆大小
     */
    private static final int mx = 1000;

    /**
     * 年轻代:年老代 1:4
     */
    private static final int newRatio = 4;

    /**
     * Eden区:Survivor区 8:1:1 总共占十份 值为eden占比 其他的由from to 平分
     */
    private static final int survivorRatio = 8;

    private static final List<String> old = new ArrayList<>();

    private static final List<String> eden = new ArrayList<>();

    private static final List<String> from = new ArrayList<>();

    private static final List<String> to = new ArrayList<>();

    private static final int oldNum;

    private static final int edenNum;

    private static final int fromNum;

    private static final int toNum;

    private static Boolean flag = true;

    static {

        oldNum = mx * newRatio / (newRatio + 1);

        edenNum = (mx * 1 / (newRatio + 1)) * survivorRatio / 10;

        fromNum = toNum = (mx * 1 / (newRatio + 1)) * (10 - survivorRatio) / 20;
    }

    public static void main(String[] args) {
        IntStream.range(0, 10000).boxed().forEach(i -> {
            try {
                if (i > 160 && i % 50 == 0) {
                    Thread.sleep(2000);
                    print();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            heap(String.valueOf(i));
        });
    }

    /**
     * 模拟堆内存分配
     * GC只会回收年轻代
     * FULL GC回收年老代 年轻代
     */
    private static void heap(String i) {
        eden.add(i);
        if (eden.size() == edenNum) {
            gc();
        }

        //先往eden中放

        //如果满了 就触发GC 回收eden 将存活数据放到from

        //再次触发GC 将所有存活数据放到to中 循环执行

        //如果存活次数标记超过15次 则将其移到年老代

        //如果年老代满了 则触发FULL GC
    }

    /**
     * 回收eden
     */
    private static void gc() {
        //回收90%以上的数据(标记不回收的地址)
        List<Integer> addressList = gcSurvivor(eden);

        if (flag) {
            for (Integer i : addressList) {
                from.add(eden.get(i));
            }
            for (Integer i : gcSurvivor(to)) {
                from.add(to.get(i));
            }
            to.clear();
            flag = false;
        } else {
            for (Integer i : addressList) {
                to.add(eden.get(i));
            }
            for (Integer i : gcSurvivor(from)) {
                to.add(from.get(i));
            }
            from.clear();
            flag = true;
        }
        //清空
        eden.clear();
        //

    }

    /**
     * 回收 Survivor 9成数据会被回收 另外一成因为
     */
    private static List<Integer> gcSurvivor(List<String> list) {
        List<Integer> integers = new ArrayList<>();
        //回收to
        for (int i = 0; i < list.size(); i++) {
            if (Math.random() * 10 > 9) {
                integers.add(i);
            }
        }
        return integers;
    }


    /**
     *
     */
    private static void print() {
        System.out.println("----------------------------------------------------------------------------");
        System.out.println("total:" + mx);
        System.out.println("old:" + "(" + old.size() + ")" + "【" + oldNum + "】" + old.toString());
        System.out.println("eden:" + "(" + eden.size() + ")" + "【" + edenNum + "】" + eden.toString());
        System.out.println("from:" + "(" + from.size() + ")" + "【" + fromNum + "】" + from.toString());
        System.out.println("to:" + "(" + to.size() + ")" + "【" + toNum + "】" + to.toString());
    }
}
